﻿using System;
using System.Diagnostics;
using System.Threading;

namespace FaceDotnet
{
    /// <summary>
    /// 表示对象池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class ObjectPool<T> where T : class
    {
        private T _firstItem;

        private readonly Element[] _items;

        private readonly Func<T> _factory;

        /// <summary>
        /// 对象池
        /// </summary>
        /// <param name="factory"></param>
        public ObjectPool(Func<T> factory)
            : this(factory, Environment.ProcessorCount * 2)
        {
        }

        /// <summary>
        /// 对象池
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="size"></param>
        public ObjectPool(Func<T> factory, int size)
        {
            Debug.Assert(size >= 1);
            _factory = factory;
            _items = new Element[size - 1];
        }

        /// <summary>
        /// 创建实例
        /// </summary>
        /// <returns></returns>
        private T CreateInstance()
        {
            T inst = _factory();
            return inst;
        }

        /// <summary>
        /// 申请实例
        /// </summary>
        /// <returns></returns>
        public T Rent()
        {
            T inst = _firstItem;
            if (inst == null || inst != Interlocked.CompareExchange(ref _firstItem, null, inst))
            {
                inst = RentSlow();
            }

            return inst;
        }

        /// <summary>
        /// 慢速申请
        /// </summary>
        /// <returns></returns>
        private T RentSlow()
        {
            Element[] items = this._items;
            for (int i = 0; i < items.Length; i++)
            {
                T inst = items[i].Value;
                if (inst != null)
                {
                    if (inst == Interlocked.CompareExchange(ref items[i].Value, null, inst))
                    {
                        return inst;
                    }
                }
            }

            return CreateInstance();
        }

        /// <summary>
        /// 退还对象
        /// </summary>
        /// <param name="obj"></param>
        public void Return(T obj)
        {
            if (_firstItem == null)
            {
                _firstItem = obj;
            }
            else
            {
                ReturnSlow(obj);
            }
        }

        /// <summary>
        /// 慢速退还
        /// </summary>
        /// <param name="obj"></param>
        private void ReturnSlow(T obj)
        {
            Element[] items = this._items;
            for (int i = 0; i < items.Length; i++)
            {
                if (items[i].Value == null)
                {
                    items[i].Value = obj;
                    break;
                }
            }
        }

        /// <summary>
        /// 表示元素
        /// </summary>

        [DebuggerDisplay("{Value,nq}")]
        private struct Element
        {
            internal T Value;
        }
    }
}
